package cn.flant.db.mgr;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.TimeUnit;

import cn.flant.db.DB;
import cn.flant.exec.CircularExecutor;
import cn.flant.util.DateUtil;

public final class DefaultManager implements ConnectionManager, Runnable {
	private CircularExecutor executor;
	private Connection conn = null;
	private DB db = null;
	private boolean isConnFree = true;
	private long lastFreeTime = System.currentTimeMillis();
	private long lastTestTime = System.currentTimeMillis();
	private long maxIdleTime;

	public DefaultManager(DB db) {
		if (db == null) {
			throw new NullPointerException();
		}
		this.db = db;
		maxIdleTime = db.getMaxIdleTime() * 1000;
		if (maxIdleTime <= 0) {
			maxIdleTime = 60 * 1000L;
		}

		executor = new CircularExecutor(this, "DefaultConnectionManagerChecker", 5000, TimeUnit.MILLISECONDS);
	}

	public void run() {
		checkConnection();
	}

	private synchronized void checkConnection() {
		if (conn == null || !isConnFree) {
			return;
		}
		try {
			if (conn.isClosed() || System.currentTimeMillis() - lastFreeTime > maxIdleTime) {
				destroy();
			} else {
				if (db.getTestQuery() != null) {
					if (System.currentTimeMillis() - lastTestTime < 30 * DateUtil.SECOND) {
						return;
					}
					Statement stmt = null;
					ResultSet rs = null;
					try {
						stmt = conn.createStatement();
						rs = stmt.executeQuery(db.getTestQuery());
					} catch (Exception e) {
						destroy();
					} finally {
						DB.close(rs);
						DB.close(stmt);
					}
					lastTestTime = System.currentTimeMillis();
				}
			}
		} catch (Exception e) {
			destroy();
		}
	}

	public synchronized void destroy() {
		if (conn != null) {
			DB.close(conn);
			conn = null;
		}
		executor.shutdown();
		isConnFree = true;
	}

	public synchronized void closeConnection(Connection conn) {
		this.lastFreeTime = System.currentTimeMillis();
		isConnFree = true;
	}

	public synchronized Connection getConnection() throws SQLException {
		if (conn == null || conn.isClosed()) {
			conn = DB.createConnection(db);
		}
		isConnFree = false;
		if (!executor.isRunning()) {
			executor.startup();
		}
		return conn;
	}

	public synchronized void reset(DB db) {
		if (db == null) {
			throw new NullPointerException();
		}
		this.db = db;
		this.conn = null;
	}

	public DB getDbInfo() {
		return db;
	}

	@Override
	public boolean isAvailable() {
		return true;
	}
}
